use crate::context_ptr::ContextPtr;
use crate::engine::Engine;
use std::ffi::{c_char, c_long, c_void};

#[no_mangle]
pub extern "C" fn engine_build(out_engine: *mut *mut Engine) {
    let engine = Engine::new();
    unsafe {
        *out_engine = Box::into_raw(Box::new(engine));
    }
}

pub type EngineStringLsr = Option<extern "C" fn(context: *const c_void, value: *const c_char)>;
pub type EngineIntLsr = Option<extern "C" fn(context: *const c_void, value: c_long)>;

pub(crate) fn ref_engine<'a>(engine: *const Engine) -> &'a Engine {
    unsafe { &*engine }
}

#[no_mangle]
pub extern "C" fn engine_set_string_listener(
    engine: *const Engine,
    context: *const c_void,
    listener_opt: EngineStringLsr,
) {
    if engine.is_null() {
        return;
    }
    if listener_opt.is_some() {
        let context_ptr = ContextPtr(context);
        ref_engine(engine).set_string_listener(Some(Box::new(move |string| {
            context_ptr.string_listener(listener_opt, string);
        })));
    } else {
        ref_engine(engine).set_string_listener(None);
    }
}

#[no_mangle]
pub extern "C" fn engine_set_int_listener(
    engine: *const Engine,
    context: *const c_void,
    listener_opt: EngineIntLsr,
) {
    if engine.is_null() {
        return;
    }
    if listener_opt.is_some() {
        let context_ptr = ContextPtr(context);
        ref_engine(engine).set_int_listener(Some(Box::new(move |int| {
            context_ptr.int_listener(listener_opt, int);
        })));
    } else {
        ref_engine(engine).set_int_listener(None);
    }
}

#[no_mangle]
pub extern "C" fn engine_call_listeners(engine: *const Engine, count: c_long, need_sleep: bool) {
    if engine.is_null() {
        return;
    }
    ref_engine(engine).call_listeners(count, need_sleep);
}
